import * as cc from 'cc';
import { GameUtils } from '../../game/utils/GameUtils';
import { Loger } from '../loger/Loger';
import SceneMgr from '../scene/SceneMgr';
import { RandomUtils } from '../utils/RandomUtils';
import { TimeUtils } from '../utils/TimeUtils';
import Utils from '../utils/Utils';
import { Singleton } from './../class_base/Singleton';
import { ResUtils } from './../utils/ResUtils';
import { ViewBase, ViewLiftCycle } from './ViewBase';
import { ViewBackState, ViewLayer, ViewMode } from './ViewConsts';
import { ViewCtrlBase } from './ViewCtrlBase';

export default class ViewMgr extends Singleton {

    private stack: string[] = [];
    private mapCtrl: Map<string, ViewCtrlBase> = new Map();

    private mapLoadingView: Map<string, boolean> = new Map();
    private mapViewPrefab: Map<string, cc.Prefab> = new Map();

    private clear() {
        this.mapCtrl.clear();
        this.stack = [];
        this.mapLoadingView.clear();
        this.mapViewPrefab.clear();
    }

    public showView<T extends ViewCtrlBase>(ctrlClass: typeof ViewCtrlBase, params?: { [key: string]: any, hideOther?: boolean, unHideNames?: string[] }): Promise<T> {
        return new Promise<T>(async (resolve, reject) => {
            if (!SceneMgr.getInstance().curScene) {
                Loger.warn(`正在切换场景,打开ui失败: viewName: ${ctrlClass.viewName}`);
                return null;
            }
            let viewId = `${ctrlClass.viewName}`;
            if (ctrlClass.viewMode == ViewMode.MULTI) {
                viewId = `${ctrlClass.viewName}_${new Date().getTime()}_${RandomUtils.rangeInt(1, 1000)}`;
            }
            let viewCtrl = this.mapCtrl.get(viewId);
            if (!viewCtrl) {
                //不存在,创建一个
                viewCtrl = new ctrlClass();
                viewCtrl.init(params)
                this.mapCtrl.set(viewId, viewCtrl);
                const uiRoot = SceneMgr.getInstance().getUIRoot(viewCtrl.viewLayer);
                let curSceneName = SceneMgr.getInstance().curScene.sceneName;
                let viewNode: cc.Node = uiRoot.getChildByName(ctrlClass.viewName);
                if (!!viewNode && ctrlClass.viewMode == ViewMode.MULTI) {
                    viewNode.destroy();
                    viewNode = null;
                }
                if (!viewNode) {
                    //节点上没有放,需要动态加载
                    if (this.mapLoadingView.has(viewId)) {
                        Loger.log(`${viewId}正在加载中...`);
                        return;
                    }
                    let beforLoad = new Date();
                    this.stack.push(viewId);
                    this.mapLoadingView.set(viewId, true);
                    const viewPrefab = await ResUtils.loadRes<cc.Prefab>(viewCtrl.viewClass.getUrl());
                    Loger.log(`load view 【${viewId}】 finish for ${TimeUtils.getDateDiff(beforLoad, new Date(), TimeUtils.str_ms)}ms`);
                    this.mapLoadingView.delete(viewId);
                    if (!this.mapCtrl.has(viewId)) {
                        if (viewPrefab) {
                            viewPrefab.decRef();
                        }
                        this.stack.splice(this.stack.indexOf(viewId), 1);
                        Loger.log(`${viewId}已经关闭,释放掉`);
                        return null;
                    }
                    viewNode = cc.instantiate(viewPrefab);
                    if (!viewNode || !SceneMgr.getInstance().curScene || SceneMgr.getInstance().curScene.sceneName !== curSceneName) {
                        const err = "正在切换场景,资源加载无效,立即释放掉";
                        Loger.log(err);
                        viewPrefab.decRef();
                        Utils.gc();
                        reject(err);
                        return null;
                    }
                    viewPrefab.addRef();
                    this.mapViewPrefab.set(ctrlClass.viewName, viewPrefab);
                    viewNode.setPosition(0, 0)
                    viewNode.setParent(uiRoot);
                    viewNode.name = viewId;
                } else {
                    //本来就放在场景中的ui,直接显示,
                    for (let i = 0; i < this.stack.length; i++) {
                        let tempCtrl = this.mapCtrl.get(this.stack[i]);
                        if (tempCtrl && tempCtrl.viewComponent) {
                            tempCtrl.viewComponent.node.setSiblingIndex(i);
                        } else {
                            this.mapCtrl.delete(this.stack[i]);
                        }
                    }
                    viewNode.active = true;
                }
                let viewCom: ViewBase = viewNode.getComponent(viewCtrl.viewClass) || viewNode.addComponent(viewCtrl.viewClass);
                viewCtrl.viewComponent = viewCom;
                viewCtrl.viewName = ctrlClass.viewName;
                viewCtrl.viewId = viewId;
                viewCom.setCtrl(viewCtrl);
                viewCom.setStartCallback(() => {
                    resolve(viewCtrl as T);
                })
            } else {
                if (!viewCtrl.viewComponent || !viewCtrl.viewComponent.checkCanOpen()) {
                    Loger.log(`${viewId} 还没有关闭,不能打开`);
                    return;
                }
                //存在,提到最上层
                //如果存在则排序层级
                this.stack.splice(this.stack.indexOf(viewId), 1);
                this.stack.push(viewId)
                viewCtrl.onClose();
                viewCtrl.init(params);
                for (let i = 0; i < this.stack.length; i++) {
                    let tempCtrl = this.mapCtrl.get(this.stack[i]);
                    if (tempCtrl && tempCtrl.viewComponent) {
                        tempCtrl.viewComponent.node.setSiblingIndex(i);
                    } else {
                        this.mapCtrl.delete(this.stack[i]);
                    }
                }
                let viewCom = viewCtrl.viewComponent;
                viewCom.node.active = true;
                viewCom.setCtrl(viewCtrl);
                viewCom.setStartCallback(() => {
                    resolve(viewCtrl as T);
                })
                viewCom.changeLifeCycle(ViewLiftCycle.OPEN);
            }
            if ((params && params.hideOther) || (viewCtrl.viewLayer == ViewLayer.Layer_Scene)) {
                if (this.mapCtrl.get(viewId)) {
                    this.mapCtrl.get(viewId).arrBeHiddenView = this._hideOtherView(viewId, params && params.unHideName);
                }
            }
        })
    }

    public hideView(viewId: string, callback?: () => void) {
        if (!this.mapCtrl.get(viewId)) {
            return;
        }
        // if (!this.mapCtrl.get(viewId).viewComponent.checkCanClose()) {
        //     Loger.log("View 正在打开,还不能关闭")
        //     return;
        // }
        const vc = this.mapCtrl.get(viewId)
        if (vc) {
            this.mapCtrl.delete(viewId);
            vc.viewComponent && vc.viewComponent.__beginClose(() => {
                this.stack.splice(this.stack.indexOf(viewId), 1);
                this.stack.unshift(viewId);
                for (let i = 0; i < this.stack.length; i++) {
                    let tempCtrl = this.mapCtrl.get(this.stack[i]);
                    if (tempCtrl && tempCtrl.viewComponent) {
                        tempCtrl.viewComponent.node.setSiblingIndex(i);
                    } else {
                        this.mapCtrl.delete(this.stack[i]);
                    }
                }
                if (callback) {
                    callback();
                }
            })
        }
    }

    public removeView(viewId: string, callback?: () => void) {
        Loger.log(`${viewId} 开始关闭`);
        const vc = this.mapCtrl.get(viewId)
        if (vc) {
            let viewName = vc.viewName;
            // if (!vc.viewComponent.checkCanClose()) {
            //     Loger.log("View 正在打开,还不能关闭")
            //     return;
            // }
            this.mapCtrl.delete(viewId);
            vc.viewComponent && vc.viewComponent.__beginRemove(() => {
                for (let i = 0; i < this.stack.length; i++) {
                    const itemName = this.stack[i];
                    if (itemName == viewId) {
                        this.stack.splice(i, 1)
                        const prefab = this.mapViewPrefab.get(viewName);
                        if (prefab) {
                            prefab.decRef()
                            if (prefab.refCount == 0) {
                                this.mapViewPrefab.delete(viewName);
                            }
                        }
                        break;
                    }
                }
                Utils.gc()
                if (callback) {
                    callback();
                }
                Loger.log(`${viewId} 关闭完成`);
            });

        }

    }
    public hideViewByName(viewName: string, callback?: () => void) {
        for (let i = 0; i < this.stack.length; i++) {
            const viewId = this.stack[i];
            if (viewId.startsWith(viewName)) {
                this.hideView(viewId, callback)
                return;
            }
        }
    }
    public removeViewByName(viewName: string, callback?: () => void) {
        for (let i = 0; i < this.stack.length; i++) {
            const viewId = this.stack[i];
            if (viewId.startsWith(viewName)) {
                this.removeView(viewId, callback)
                return;
            }
        }
    }


    public removeAllView() {
        this.mapCtrl.forEach((value, key) => {
            value.viewComponent.setDestory();
            ResUtils.releaseRes(this.mapViewPrefab.get(key));
        })
        this.clear()
    }

    public removeAllViewExcept(...exceptName: string[]) {
        for (let i = 0; i < this.stack.length; i++) {
            const viewId = this.stack[i];
            if (exceptName.indexOf(viewId) >= 0) {
                continue;
            }
            this.removeView(viewId);
        }
    }

    public hideAllView() {
        this.mapCtrl.forEach((value, key) => {
            value.close(false);
        })
    }

    public hideAllViewExcept(...exceptName: string[]) {
        for (let i = 0; i < this.stack.length; i++) {
            const viewId = this.stack[i];
            if (exceptName.indexOf(viewId) >= 0) {
                continue;
            }
            this.hideView(viewId);
        }
    }


    public getTopView(): ViewCtrlBase {
        return this.mapCtrl[this.stack[this.stack.length - 1]]
    }

    public getView<T extends ViewCtrlBase>(viewName: string): T {
        if (this.mapCtrl.has(viewName)) {
            return this.mapCtrl.get(viewName) as T;
        }
        for (let i = this.stack.length - 1; i >= 0; i--) {
            const viewId = this.stack[i];
            if (this.mapCtrl.has(viewId) && this.mapCtrl.get(viewId).viewName === viewName) {
                return this.mapCtrl.get(viewId) as T;
            }
        }
        return null;
    }

    /**判断UI是不是在最高层 */
    public isTopView(viewName: string): boolean {
        const topViewName = this.getTopView().viewName;
        if (topViewName == viewName) {
            return true
        }
        if (topViewName.startsWith(viewName)) {
            return true;
        }
        return false;
    }

    /**尝试关闭最上层ui,用于返回键 */
    public tryRemoveTopView() {
        for (let i = this.stack.length - 1; i >= 0; i--) {
            const viewId = this.stack[i];
            if (this.mapCtrl.has(viewId)) {
                const vc = this.mapCtrl.get(viewId);
                const canBack = vc.checkCanBack();
                if (canBack === ViewBackState.Close) {
                    vc.close();
                    return true;
                } else if (canBack === ViewBackState.Return) {
                    return false;
                } else if (canBack === ViewBackState.ShowExitGame) {
                    GameUtils.showExitGame();
                    return false;
                }
                else {
                    continue;
                }
            }
        }
        return false;
    }

    /**隐藏其他ui */
    private _hideOtherView(curViewId: string, unHideName?: string[]): string[] {
        let arrHide = [];
        for (let i = 0; i < this.stack.length; i++) {
            const viewId = this.stack[i];
            let isUnHide = false;
            if (unHideName && unHideName.length >= 0) {
                for (let k = 0; k < unHideName.length; k++) {
                    if (viewId === curViewId || viewId.startsWith(unHideName[k])) {
                        isUnHide = true;
                        break;
                    }
                }
            }
            if (viewId === curViewId || isUnHide) {
                continue;
            }
            const ctrl = this.mapCtrl.get(viewId);
            if (!ctrl || !ctrl.checkCanHideByHideOther()) {
                continue;
            }
            const viewCom = ctrl.viewComponent;
            if (viewCom && viewCom.node && viewCom.node.active) {
                viewCom.node.active = false;
                // viewCom.changeLifeCycle(ViewLiftCycle.CLOSE);          
                arrHide.push(viewId);
            }
        }
        return arrHide;
    }

    /**激活被隐藏的ui */
    public activeBeHiddenView(arrViewId: string[]) {
        for (let i = 0; i < arrViewId.length; i++) {
            const viewId = arrViewId[i];
            const viewCtrl = this.mapCtrl.get(viewId);
            if (viewCtrl) {
                const viewCom = this.mapCtrl.get(viewId).viewComponent;
                if (viewCom && viewCom.node) {
                    viewCom.node.active = true;
                    viewCom.changeLifeCycle(ViewLiftCycle.OPEN);
                }
            }
        }
    }

}